﻿/*
 * KLF Headless Browser
 * Written by Kris Oye <kristianoye@gmail.com>
 * Copyright (C) 2011-2012.  All rights reserved.
 */
namespace KLF.Lib.Browser.DOM.HTML
{
    #region Using Statements

    using System;
    using KLF.Lib.Browser.DOM.Events;

    #endregion

    public class HTMLInputElement : HTMLControlBase
    {
        #region Constructors

        internal HTMLInputElement(Document owner)
            : base(owner, HTMLConstants.HTML_TAG_INPUT)
        {
        }

        #endregion

        #region DOM Level 2 HTML Compliance

        /// <summary>
        /// A comma-separated list of content types that a server processing
        /// this form will handle correctly. See the accept attribute 
        /// definition in HTML 4.01.
        /// </summary>
        public string accept
        {
            get
            {
                return getAttribute(HTMLConstants.HTML_ATT_ACCEPT);
            }
            set
            {
                if (String.IsNullOrEmpty(value))
                    removeAttribute(HTMLConstants.HTML_ATT_ACCEPT);
                else
                    setAttribute(HTMLConstants.HTML_ATT_ACCEPT, value);
            }
        }

        /// <summary>
        /// Aligns this object (vertically or horizontally) with respect to its
        /// surrounding text. See the align attribute definition in HTML 4.01. 
        /// This attribute is deprecated in HTML 4.01.
        /// </summary>
        [Obsolete]
        public string align
        {
            get
            {
                return getAttribute(HTMLConstants.HTML_ATT_ALIGN);
            }
            set
            {
                if (String.IsNullOrEmpty(value))
                    removeAttribute(HTMLConstants.HTML_ATT_ALIGN);
                else
                    setAttribute(HTMLConstants.HTML_ATT_ALIGN, value);
            }
        }

        /// <summary>
        /// Alternate text for user agents not rendering the normal content 
        /// of this element. See the alt attribute definition in HTML 4.01.
        /// </summary>
        public string alt
        {
            get
            {
                return getAttribute(HTMLConstants.HTML_ATT_ALT);
            }
            set
            {
                if (String.IsNullOrEmpty(value))
                    removeAttribute(HTMLConstants.HTML_ATT_ALT);
                else
                    setAttribute(HTMLConstants.HTML_ATT_ALT, value);
            }
        }

        /// <summary>
        /// When the type attribute of the element has the value "radio" or 
        /// "checkbox", this represents the current state of the form control, 
        /// in an interactive user agent. Changes to this attribute change the 
        /// state of the form control, but do not change the value of the HTML 
        /// checked attribute of the INPUT element.
        /// 
        /// Note: During the handling of a click event on an input element with 
        /// a type attribute that has the value "radio" or "checkbox", some
        /// implementations may change the value of this property before the 
        /// event is being dispatched in the document. If the default action of 
        /// the event is canceled, the value of the property may be changed back 
        /// to its original value. This means that the value of this property 
        /// during the handling of click events is implementation dependent.
        /// </summary>
        public bool @checked
        {
            get
            {
                return HTMLBool.Eval(
                    getAttribute(HTMLConstants.HTML_ATT_CHECKED),
                    HTMLConstants.HTML_ATT_CHECKED);
            }
            set
            {
                if (value && type != null && type == HTMLConstants.HTML_INPUT_RADIO)
                {
                    var list = form.elements;
                    for (int i = 0; i < list.length; i++)
                    {
                        HTMLInputElement ctrl = list.item(i) as HTMLInputElement;
                        if (ctrl != null)
                        {
                            if (ctrl.name == this.name && ctrl != this)
                            {
                                ctrl.@checked = false;
                            }
                        }
                    }
                }
                if (!HTMLBool.Eval(value.ToString(), HTMLConstants.HTML_ATT_CHECKED))
                    removeAttribute(HTMLConstants.HTML_ATT_CHECKED);
                else
                    setAttribute(HTMLConstants.HTML_ATT_CHECKED, HTMLConstants.HTML_ATT_CHECKED);
            }
        }

        /// <summary>
        /// Simulates clicking
        /// </summary>
        public override void click()
        {
            base.click();

            if (type == HTMLConstants.HTML_BUTTON_SUBMIT)
            {
                if (form != null)
                    form.submit(this);
            }
            else if (type == HTMLConstants.HTML_BUTTON_RESET)
            {
            }
            else if (type == HTMLConstants.HTML_INPUT_CHECKBOX ||
                type == HTMLConstants.HTML_INPUT_RADIO)
            {
                @checked = !@checked;
                dispatchEvent(new Event(HTMLConstants.HTML_EVENT_CHANGE, new
                {
                    bubbles = true,
                    cancelable = false,
                    target = this as IEventTarget
                }));
            }
        }

        /// <summary>
        /// Maximum number of characters for text fields, when type has the 
        /// value "text" or "password". See the maxlength attribute definition 
        /// in HTML 4.01.
        /// </summary>
        public int maxLength
        {
            get
            {
                return getAttributeAsType<int>(HTMLConstants.HTML_ATT_MAXLENGTH, (int)Int16.MaxValue);
            }
            set
            {
                if (value > -1)
                    setAttributeAsType<int>(HTMLConstants.HTML_ATT_MAXLENGTH, value);
            }
        }

        /// <summary>
        /// This control is read-only. Relevant only when type has the value
        /// "text" or "password". See the readonly attribute definition in 
        /// HTML 4.01
        /// </summary>
        public bool readOnly
        {
            get
            {
                String test = getAttribute(HTMLConstants.HTML_ATT_READONLY).ToLowerInvariant();

                return (
                    test.Equals(HTMLConstants.HTML_ATT_READONLY) || 
                    test.Equals("true") || 
                    test.Equals("1"));
            }
            set
            {
                if (!value)
                    removeAttribute(HTMLConstants.HTML_ATT_READONLY);
                else
                    setAttribute(HTMLConstants.HTML_ATT_READONLY, HTMLConstants.HTML_ATT_READONLY);
            }
        }

        /// <summary>
        /// Select the contents of the text area. For INPUT elements whose 
        /// type attribute has one of the following values: "text", "file", 
        /// or "password". 
        /// </summary>
        public void select()
        {
            // Does nothing, really
        }

        /// <summary>
        /// Size information. The precise meaning is specific to each type of 
        /// field. See the size attribute definition in HTML 4.01.
        /// </summary>
        public int size
        {
            get
            {
                return getAttributeAsType<int>(HTMLConstants.HTML_ATT_SIZE, 0);
            }
            set
            {
                if (value > 0)
                    setAttributeAsType<int>(HTMLConstants.HTML_ATT_SIZE, value);
            }
        }

        /// <summary>
        /// The type of control created (all lower case). See the type 
        /// attribute definition in HTML 4.01.
        /// </summary>
        public string type
        {
            get
            {
                String s = getAttribute(HTMLConstants.HTML_ATT_TYPE);
                return String.IsNullOrEmpty(s) ? "text" : s;
            }
            set
            {
                if (String.IsNullOrEmpty(value))
                    removeAttribute(HTMLConstants.HTML_ATT_TYPE);
                else
                    setAttribute(HTMLConstants.HTML_ATT_TYPE, value);
            }
        }

        /// <summary>
        /// Use client-side image map. See the usemap attribute
        /// definition in HTML 4.01.
        /// </summary>
        public string useMap
        {
            get
            {
                return getAttribute(HTMLConstants.HTML_ATT_USEMAP);
            }
            set
            {
                if (String.IsNullOrEmpty(value))
                    removeAttribute(HTMLConstants.HTML_ATT_USEMAP);
                else
                    setAttribute(HTMLConstants.HTML_ATT_USEMAP, value);
            }
        }

        public override string value
        {
            get
            {
                string v = base.value;

                if (String.IsNullOrEmpty(v))
                    return type.Equals(HTMLConstants.HTML_CONTROL_CHECKBOX) ? "on" : "";
                else
                    return v;
            }
            set
            {
                base.value = value;
            }
        }
        #endregion

        internal override void SimulateInput(string val)
        {
            if (type == HTMLConstants.HTML_INPUT_CHECKBOX|| 
                type == HTMLConstants.HTML_INPUT_RADIO)
            {
                if (val == value)
                    click();
                return;
            }
            base.SimulateInput(val);
        }
    }
}
